#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <PubSubClient.h>
#include <ESP8266WebServer.h>
#include <Wire.h>
#include <Adafruit_AHTX0.h>
#include <Adafruit_BMP280.h>
#include <Ticker.h>

const char* AP_NAME = "ESP8266";
const char* page_html = "<html><head><title>ESP8266 WiFi Setup</title></head><body><form action=\"/\" method=\"post\">SSID:<br><input type=\"text\" name=\"ssid\"><br>Password:<br><input type=\"text\" name=\"pwd\"><br><input type=\"submit\" value=\"Submit\"></form></body></html>";

const char* mqtt_server = "mqtts.heclouds.com";
const char* cid = "esp8266_mqtt";//OneNET平台创建的设备名称
const char* pid = "xxxxxx";//OneNET平台创建的产品ID
const char* pwd = "version=2018-10-31&res=products%2Fxxxxxx&et=4102415999&method=md5&sign=xxxxxxxxxxxxxxxxxxx";//OneNET平台生产的token
const char* topic_dp = "$sys/xxxxxx/esp8266_mqtt/dp/post/json"; //推送数据流
const char* topic_dp_msg = "{'id':8266,'dp':{'T':[{'v':%f}],'H':[{'v':%f}],'P':[{'v':%f}]}}";//数据流格式
const char* topic_cmd = "$sys/xxxxxx/esp8266_mqtt/cmd/request/+"; //命令订阅
const char* topic_cmd_re = "$sys/xxxxxx/esp8266_mqtt/cmd/response/"; //命令应答
const char* topic_image = "$sys/xxxxxx/esp8266_mqtt/image/update"; //更新属性
const char* topic_image_msg = "{'state':{'reported':{'T':%f,'H':%f,'P':%f}}}";//更新内容

ESP8266WiFiMulti wifiMulti;
WiFiClient espwifi;
PubSubClient mqtt(espwifi);
IPAddress apIP(192, 168, 4, 1);//esp8266-AP IP地址
ESP8266WebServer webserver(80);//创建WebServer

Adafruit_AHTX0 aht;
Adafruit_BMP280 bmp;
bool aht_state = false;           // aht20状态（初始化是否成功）
bool bmp_state = false;           // bmp280状态（初始化是否成功）
bool i2c_state = false; //i2c状态 防止占用

Ticker ticker;//定时器 用来定时推送传感器数据

bool resetwifi = false;//重设wifi已连接
char wifi_ssid[32] = {0};
char wifi_pwd[64] = {0};


//设置wifi
void set_wifi() {
  WiFi.hostname(AP_NAME);
  WiFi.mode(WIFI_STA);
  WiFi.persistent(true);
  WiFi.setAutoConnect(true);//设置自动连接
  //Serial.println("Connecting Wifi...");
  if (resetwifi) {
    wifiMulti.cleanAPlist();//清除原有的接入点列表   
    wifiMulti.addAP(wifi_ssid, wifi_pwd);//添加接入点
  }
  if (wifiMulti.run() == WL_CONNECTED) {
    //Serial.print("WiFi connected: ");
    //Serial.println(WiFi.SSID());
    //Serial.println(WiFi.localIP());
    resetwifi = false;
    set_mqtt();      
  }else{
    set_wifi_ap();
  }
}
//设置wifi ap模式
void set_wifi_ap() {
  resetwifi = true;   
  //Serial.println("");
  //Serial.println("WiFi is not connected!");
  WiFi.mode(WIFI_AP); 
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
  if (WiFi.softAP(AP_NAME)) {
    //Serial.println("ESP8266 AP is started!");
    set_webserver();//启用web配网
  }
}

//访问主页回调函数
void web_handleRoot() {
  webserver.send(200, "text/html", page_html);
}

//Post回调函数
void web_handleRootPost() {
  if (webserver.hasArg("ssid")) {//判断是否有账号参数
    strcpy(wifi_ssid, webserver.arg("ssid").c_str());//将账号参数拷贝到sta_ssid中
    //Serial.print("got ssid:");
    //Serial.println(wifi_ssid);
  } else {//没有参数
    webserver.send(200, "text/html", "error, not found ssid");//返回错误页面
    return;
  }
  //密码
  if (webserver.hasArg("pwd")) {
    strcpy(wifi_pwd, webserver.arg("pwd").c_str());
    //Serial.print("got password:");
    //Serial.println(wifi_pwd);
  } else {
    webserver.send(200, "text/html", "error, not found password");
    return;
  }

  webserver.send(200, "text/html", "OK! Connecting Wifi...");//返回保存成功页面
  //保存wifi配置
  delay(1000);
  webserver.stop();//停止webserver服务
  resetwifi = true; 
  set_wifi();//重新设置wifi
}

//设置webserver
void set_webserver() {
  webserver.on("/", HTTP_GET, web_handleRoot);//设置主页回调函数
  webserver.on("/", HTTP_POST, web_handleRootPost);//设置Post请求回调函数
  webserver.begin();//启动WebServer
  //Serial.println("WebServer is started!");
}

//mqtt设置
void set_mqtt() {
  mqtt.setServer(mqtt_server, 1883);
  mqtt.setCallback(mqtt_callback);
}

void ticker_callback(){
  mqtt_pubmsg(topic_dp, topic_dp_msg);
}

void mqtt_pubmsg(const char* topic, const char* msg_f){
  if(!i2c_state){
    i2c_state = true;
    char msg[256] = {0};
    sensors_event_t humAHT, tempAHT;
    aht.getEvent(&humAHT, &tempAHT);
    snprintf(msg, sizeof(msg), msg_f, tempAHT.temperature, humAHT.relative_humidity, bmp.readPressure());
    mqtt.publish(topic, msg);
    Serial.println(msg);
    i2c_state = false;
  }
}

void mqtt_callback(char* topic, byte* payload, uint length) {
  char topic_re[128];
  memset(topic_re, '\0', sizeof(topic_re));
  strcpy(topic_re, topic_cmd_re);
  strncat(topic_re, topic + 37, 36);
  mqtt.publish(topic_re, "");
  mqtt_pubmsg(topic_image, topic_image_msg);
}

void mqtt_reconnect() {
  while (!mqtt.connected()) {
    //Serial.print("MQTT connecting...");
    if (mqtt.connect(cid, pid, pwd)) {
      //Serial.println("MQTT connected");
      mqtt.subscribe(topic_cmd);
      //mqtt.publish(topic_image, led_off_msg);
      
      ticker.attach(15, ticker_callback); //15秒推送一次
    } else {
      //Serial.print("failed, rc=");
      //Serial.print(mqtt.state());
      //Serial.println(" try again in 5 seconds");
      ticker.detach();  // 使用detach来停止ticker对象定时调用函数
      delay(5000);
    }
  }
}

void set_i2c(){
  delay(3000);
  Serial.println("Init I2C ...");
	Wire.begin(0, 2);//重定义I2C端口(SDA=2,SCL=0)
	aht_state = aht.begin();
	if(aht_state){
    sensors_event_t humidity, temp;
    aht.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
    Serial.print("AHT20 Temperature: ");
    Serial.println(temp.temperature);
    Serial.print("AHT20 Humidity: ");
    Serial.println(humidity.relative_humidity);
  }else{
    Serial.println("Didn't find AHT20");
  }
	bmp_state = bmp.begin();//bmp.begin(BMP280_ADDRESS_ALT)
	if (bmp_state) {
    bmp.setSampling(Adafruit_BMP280::MODE_FORCED, // FORCE模式读完自动转换回sleep模式
      Adafruit_BMP280::SAMPLING_X1,
      Adafruit_BMP280::SAMPLING_X4,
      Adafruit_BMP280::FILTER_X16,
      Adafruit_BMP280::STANDBY_MS_1);
    Serial.print("BMP280 Temperature: ");
    Serial.println(bmp.readTemperature());
    Serial.print("BMP280 Pressure: ");
    Serial.println(bmp.readPressure());
	}else{
		Serial.println("Didn't find BMP280");
  }
}

void setup() {
  Serial.begin(115200);
  set_i2c();
  set_wifi();
}

void loop() {
  if (!resetwifi) {
    if (mqtt.connected()) {
      mqtt.loop();
    }else if (wifiMulti.run() == WL_CONNECTED) {
      mqtt_reconnect();
    }else{
      set_wifi_ap();
    }
  } else {
    webserver.handleClient();
  }
}
